package jinyilw.swing.visuals.border;

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.geom.Path2D;

import javax.swing.JLabel;
import javax.swing.UIManager;
import javax.swing.border.AbstractBorder;
import javax.swing.border.Border;
import javax.swing.plaf.basic.BasicHTML;

import jinyilw.swing.i18n.ICSetting;

public class TitledBorder extends AbstractITitleBorder
{
	private static final long serialVersionUID = 5957108864424033319L;
	protected Border border;
	protected JLabel label;

	public TitledBorder()
	{

	}
	public TitledBorder(String title)
	{
		this(null, title, LEADING, DEFAULT_POSITION, null, null);
	}

	public TitledBorder(Border border)
	{
		this(border, "", LEADING, DEFAULT_POSITION, null, null);
	}

	public TitledBorder(Border border, String title)
	{
		this(border, title, LEADING, DEFAULT_POSITION, null, null);
	}

	public TitledBorder(ICSetting iTitle)
	{
		this(null, iTitle, LEADING, DEFAULT_POSITION, null, null);
	}

	public TitledBorder(Border border, String title, int titleJustification,
			int titlePosition)
	{
		this(border, title, titleJustification, titlePosition, null, null);
	}

	public TitledBorder(Border border, String title, int titleJustification,
			int titlePosition, Font titleFont)
	{
		this(border, title, titleJustification, titlePosition, titleFont, null);
	}

	public TitledBorder(Border border, String title, int titleJustification,
			int titlePosition, Font titleFont, Color titleColor)
	{
		this.title = title;
		this.border = border;
		this.titleFont = titleFont;
		this.titleColor = titleColor;

		setTitleJustification(titleJustification);
		setTitlePosition(titlePosition);

		label = createLabel();
		label.setOpaque(false);
		label.putClientProperty(BasicHTML.propertyKey, null);
	}

	public TitledBorder(Border border, ICSetting iTitle, int titleJustification,
			int titlePosition, Font titleFont, Color titleColor)
	{
		this(border, (String) null, titleJustification, titlePosition,
				titleFont, titleColor);
		i18nTitle = iTitle;
	}

	protected JLabel createLabel()
	{
		return new JLabel();
	}

	@Override
	public void paintBorder(Component c, Graphics g, int x, int y, int w, int h)
	{
		ownerComponent = c;
		Border border = getBorder();
		String title = getTitle();
		if ((title != null) && !title.isEmpty())
		{
			int edge = (border instanceof TitledBorder) ? 0 : EDGE_SPACING;
			JLabel label = getLabel(c);
			Dimension size = label.getPreferredSize();
			Insets insets = getBorderInsets(border, c, new Insets(0, 0, 0, 0));

			int borderX = x + edge;
			int borderY = y + edge;
			int borderW = w - edge - edge;
			int borderH = h - edge - edge;

			int labelY = y;
			int labelH = size.height;
			int position = getPosition();
			switch (position)
			{
				case ABOVE_TOP:
					insets.left = 0;
					insets.right = 0;
					borderY += labelH - edge;
					borderH -= labelH - edge;
					break;
				case TOP:
					insets.top = edge + insets.top / 2 - labelH / 2;
					if (insets.top < edge)
					{
						borderY -= insets.top;
						borderH += insets.top;
					} else
					{
						labelY += insets.top;
					}
					break;
				case BELOW_TOP:
					labelY += insets.top + edge;
					break;
				case ABOVE_BOTTOM:
					labelY += h - labelH - insets.bottom - edge;
					break;
				case BOTTOM:
					labelY += h - labelH;
					insets.bottom = edge + (insets.bottom - labelH) / 2;
					if (insets.bottom < edge)
					{
						borderH += insets.bottom;
					} else
					{
						labelY -= insets.bottom;
					}
					break;
				case BELOW_BOTTOM:
					insets.left = 0;
					insets.right = 0;
					labelY += h - labelH;
					borderH -= labelH - edge;
					break;
			}
			insets.left += edge + TEXT_INSET_H;
			insets.right += edge + TEXT_INSET_H;

			int labelX = x;
			int labelW = w - insets.left - insets.right;
			if (labelW > size.width)
				labelW = size.width;
			switch (getJustification(c))
			{
				case LEFT:
					labelX += insets.left;
					break;
				case RIGHT:
					labelX += w - insets.right - labelW;
					break;
				case CENTER:
					labelX += (w - labelW) / 2;
					break;
			}

			if (border != null)
			{
				if ((position != TOP) && (position != BOTTOM))
				{
					border.paintBorder(c, g, borderX, borderY, borderW,
							borderH);
				} else
				{
					Graphics g2 = g.create();
					if (g2 instanceof Graphics2D)
					{
						Graphics2D g2d = (Graphics2D) g2;
						Path2D path = new Path2D.Float();
						path.append(new Rectangle(borderX, borderY, borderW,
								labelY - borderY), false);
						path.append(new Rectangle(borderX, labelY,
										labelX - borderX - TEXT_SPACING, labelH),
								false);
						path.append(
								new Rectangle(labelX + labelW + TEXT_SPACING,
										labelY, borderX - labelX + borderW
										- labelW - TEXT_SPACING,
										labelH),
								false);
						path.append(
								new Rectangle(borderX, labelY + labelH, borderW,
										borderY - labelY + borderH - labelH),
								false);
						g2d.clip(path);
					}
					border.paintBorder(c, g2, borderX, borderY, borderW,
							borderH);
					g2.dispose();
				}
			}
			g.translate(labelX, labelY);
			label.setSize(labelW, labelH);
			label.paint(g);
			g.translate(-labelX, -labelY);
		} else if (border != null)
		{
			border.paintBorder(c, g, x, y, w, h);
		}
	}

	@Override
	public Insets getBorderInsets(Component c)
	{
		Insets insets = new Insets(0, 0, 0, 0);
		Border border = getBorder();
		insets = getBorderInsets(border, c, insets);
		String title = getTitle();
		if ((title != null) && !title.isEmpty())
		{
			int edge = (border instanceof TitledBorder) ? 0 : EDGE_SPACING;
			JLabel label = getLabel(c);
			Dimension size = label.getPreferredSize();

			switch (getPosition())
			{
				case ABOVE_TOP:
					insets.top += size.height - edge;
					break;
				case TOP:
				{
					if (insets.top < size.height)
						insets.top = size.height - edge;
					break;
				}
				case BELOW_TOP:
					insets.top += size.height;
					break;
				case ABOVE_BOTTOM:
					insets.bottom += size.height;
					break;
				case BOTTOM:
				{
					if (insets.bottom < size.height)
						insets.bottom = size.height - edge;
					break;
				}
				case BELOW_BOTTOM:
					insets.bottom += size.height - edge;
					break;
			}
			insets.top += edge + TEXT_SPACING;
			insets.left += edge + TEXT_SPACING;
			insets.right += edge + TEXT_SPACING;
			insets.bottom += edge + TEXT_SPACING;
		}
		return insets;
	}

	public Border getBorder()
	{
		return border != null
				? border
				: UIManager.getBorder("TitledBorder.border");
	}

	public void setBorder(Border border)
	{
		this.border = border;
	}

	protected int getPosition()
	{
		int position = getTitlePosition();
		if (position != DEFAULT_POSITION)
		{
			return position;
		}
		Object value = UIManager.get("TitledBorder.position");
		if (value instanceof Integer)
		{
			int i = (Integer) value;
			if ((0 < i) && (i <= 6))
				return i;
		} else if (value instanceof String)
		{
			String s = (String) value;
			if (s.equalsIgnoreCase("ABOVE_TOP"))
				return ABOVE_TOP;
			if (s.equalsIgnoreCase("TOP"))
				return TOP;
			if (s.equalsIgnoreCase("BELOW_TOP"))
				return BELOW_TOP;
			if (s.equalsIgnoreCase("ABOVE_BOTTOM"))
				return ABOVE_BOTTOM;
			if (s.equalsIgnoreCase("BOTTOM"))
				return BOTTOM;
			if (s.equalsIgnoreCase("BELOW_BOTTOM"))
				return BELOW_BOTTOM;
		}
		return TOP;
	}

	protected int getJustification(Component c)
	{
		int justification = getTitleJustification();
		if ((justification == LEADING)
				|| (justification == DEFAULT_JUSTIFICATION))
		{
			return c.getComponentOrientation().isLeftToRight() ? LEFT : RIGHT;
		}
		if (justification == TRAILING)
		{
			return c.getComponentOrientation().isLeftToRight() ? RIGHT : LEFT;
		}
		return justification;
	}

	protected Color getColor(Component c)
	{
		Color color = getTitleColor();
		if (color != null)
			return color;
		return (c != null) ? c.getForeground() : null;
	}

	private JLabel getLabel(Component c)
	{
		label.setText(getTitle());
		label.setFont(getFont(c));
		label.setForeground(getColor(c));
		label.setComponentOrientation(c.getComponentOrientation());
		label.setEnabled(c.isEnabled());
		return label;
	}

	private static Insets getBorderInsets(Border border, Component c,
			Insets insets)
	{
		if (border == null)
		{
			insets.set(0, 0, 0, 0);
		} else if (border instanceof AbstractBorder)
		{
			AbstractBorder ab = (AbstractBorder) border;
			insets = ab.getBorderInsets(c, insets);
		} else
		{
			Insets i = border.getBorderInsets(c);
			insets.set(i.top, i.left, i.bottom, i.right);
		}
		return insets;
	}
	@Override
	protected Component getOwnerComponent()
	{
		return ownerComponent;
	}
}
